/////////////////////////////////////////////////////////////////////////////
// Name:        ax_ui_adaptation.h
// Purpose:     Adapts controls to self-voicing
// Author:      Julian Smart
// Modified by:
// Created:     2009-03-20
// RCS-ID:
// Copyright:   (c) Julian Smart
// Licence:     New BSD License
/////////////////////////////////////////////////////////////////////////////

#ifndef _AX_UI_ADAPTATION_H_
#define _AX_UI_ADAPTATION_H_

#include "wx/dynarray.h"
#include "ax/ax_speech.h"

class AxTextEvtHandler;
class wxShortcutManager;

/**
    Type for GetContent
 */

typedef enum {
    AxContentFull,
    AxContentSummary
} AxContentLevel;

/**
    Values for flags argument to AxSelfVoicing::Adapt
 */

typedef enum {
    AxAdaptDefault =            0x0000,
    AxAdaptSetAccelerators =    0x0001
};

/**
    @class AxAdapter

    A base class for control-specific event handlers that process events controls
    and emit speech, as appropriate.

    AxAdapter also generates shortcut commands from key presses,
    and provides some utility functions such as Say.

    @category{ax}
 */

class AxAdapter: public wxEvtHandler
{
    DECLARE_CLASS( AxAdapter )
    DECLARE_EVENT_TABLE()

public:
// Constructors

    AxAdapter(wxWindow* window = NULL, AxSpeech* speech = NULL);

    ~AxAdapter();

// Operations

    void Init();

    /// Override to return the name of the control, e.g. button, listbox.
    virtual wxString GetName() = 0;

    /// Override to return the label for the control, e.g. button label or static text before a listbox.
    virtual wxString GetLabel();

    /// Call to set an alternate label for the item, for example for buttons that
    /// don't have a speakable label. If the stored label is empty, the existing label will
    /// be used or deduced from a static control nearby.
    void SetAlternateLabel(const wxString& label) { m_alternateLabel = label;}

    /// Returns the alternate label (see SetAlternateLabel).
    const wxString& GetAlternateLabel() const { return m_alternateLabel;}

    /// Override to return help on the current control.
    /// By default, wxWindow::GetHelpText is used.
    virtual wxString GetHelpText();

    /// Override to return text representing the state of the control,
    /// for example the current value.
    virtual wxString GetStateText(AxContentLevel level) = 0;

    /// Get content
    virtual wxString GetContent(AxContentLevel level) = 0;

    /// Update the appearance - text size, background, etc.
    /// By default, update the font.
    virtual bool UpdateAppearance(int hints);

    /// Called after children have been updated
    virtual bool PostUpdateAppearance(int WXUNUSED(hints)) { return false; }

    /// Override this if the adapter's actual window might not be the top-level control
    /// (may be the case for composite controls).
    virtual wxWindow* GetMainControl() const { return GetWindow(); }

    /// Override and return false from this function if the control shouldn't
    /// be voiced.
    virtual bool IsVoiced() const { return true; }

    /// Describe control current state. If metaInfo is true, describes the
    /// kind of control it is.
    /// This is a default implementation that can be overridden to
    /// give more specific detail.
    virtual bool DescribeControlContext(bool metaInfo = true, AxContentLevel level = AxContentSummary);

    /// Describe this window and all its children.
    virtual bool DescribeContainerWindow(AxContentLevel level = AxContentSummary);

    /// Get descriptions for this window, recursively. Only describe
    /// visible windows.
    virtual bool DescribeWindowRecursively(wxWindow* window, AxContentLevel level, wxString& text);

    /// Say some text
    virtual bool Say(const wxString& text, int flags = AxSpeechFlagsPurge, int verbosity = 2);

    /// Say some text
    virtual bool Say(AxSpeechChannel channel, const wxString& text, int flags = AxSpeechFlagsPurge, int verbosity = 2);

    /// Stop speaking
    virtual bool StopSpeech(AxSpeechChannel channel = AxSpeechDefaultChannel);

    /// Pause or resume speech
    virtual bool PauseOrResumeSpeech(AxSpeechChannel channel = AxSpeechDefaultChannel);

    /// Skip forward
    virtual bool SkipForward(AxSpeechChannel channel = AxSpeechDefaultChannel);

    /// Skip back
    virtual bool SkipBack(AxSpeechChannel channel = AxSpeechDefaultChannel);

    /// Are we speaking?
    virtual bool IsSpeaking(AxSpeechChannel channel = AxSpeechDefaultChannel) const;

    /// Can we stop?
    virtual bool CanStop(AxSpeechChannel channel = AxSpeechDefaultChannel) const;

    /// Can we pause?
    virtual bool CanPause(AxSpeechChannel channel = AxSpeechDefaultChannel) const;

    /// Can we resume?
    virtual bool CanResume(AxSpeechChannel channel = AxSpeechDefaultChannel) const;

    void SetSpeech(AxSpeech* speech) { m_speech = speech; }
    AxSpeech* GetSpeech() const { return m_speech; }

    void SetWindow(wxWindow* win) { m_window = win; }
    wxWindow* GetWindow() const { return m_window; }

    void SetShortcutManager(wxShortcutManager* shortcutManager) { m_shortcutManager = shortcutManager; }
    wxShortcutManager* GetShortcutManager() const { return m_shortcutManager; }

    void OnKeyDown(wxKeyEvent& event);
    void OnChar(wxKeyEvent& event);
    void OnIdle(wxIdleEvent& event);
    void OnSetFocus(wxFocusEvent& event);

    // These will need to be adapted for non-European languages.
    // For example, we should treat a Chinese character as a word,
    // and I don't know how they punctuate.
    static bool IsPunctuation(const wxChar& ch);
    static bool IsSentenceEnd(const wxChar& ch);
    static bool IsWhitespace(const wxChar& ch);

    /// Remove trailing punctuation
    static bool RemoveTrailingPunctuation(wxString& str);

    /// Add a full stop to the string, unless there's already one there.
    /// Remove extra punctuation, e.g. semicolon.
    static bool AddStop(wxString& str, const wxString& punct = wxT("."));

    /// Add a semicolon to the string to create a pause, unless there's already one there.
    /// Remove extra punctuation.
    static bool AddPause(wxString& str, const wxString& punct = wxT(";"));

    /// Returns a speakable string for the key pressed
    static wxString GetSpokenTextForKeycode(int keycode);

    /// Find a static text control that might describe this control.
    virtual wxString GetStaticLabel();

    /// Get a possible label using wxControl::GetLabel
    virtual wxString GetControlLabel();

    /// Get the AxAdapter for the focused control,
    /// or if not found, return 'this'.
    virtual AxAdapter* FindFocusedAdapter();

    /// Apply the current font size to the window
    virtual bool ApplyFontSize();

    /// Apply window colours
    virtual bool ApplyWindowColours();

    /// Sets the speech channel to be used by this adapter.
    void SetChannel(int channel) { m_channel = channel; }

    /// Returns the speech channel to be used by this adapter.
    AxSpeechChannel GetChannel(AxSpeechChannel channel = AxSpeechDefaultChannel) const;

    void OnDescribeContext(wxCommandEvent& event);
    void OnDescribeContainer(wxCommandEvent& event);
    void OnStopSpeech(wxCommandEvent& event);
    void OnPauseResumeSpeech(wxCommandEvent& event);
    void OnSkipForward(wxCommandEvent& event);
    void OnSkipBack(wxCommandEvent& event);
    void OnContextHelp(wxCommandEvent& event);
    void OnSysColourChanged(wxSysColourChangedEvent& event);
    void OnGoHome(wxCommandEvent& event);
    void OnGoUp(wxCommandEvent& event);

protected:

    wxWindow*           m_window;
    AxSpeech*           m_speech;
    wxShortcutManager*  m_shortcutManager;
    AxSpeechChannel     m_channel;
    wxString            m_alternateLabel;
};

/**
    @class AxVoiceAdaptationHandler

    Base class for handlers that know which AxAdapter class to use for
    particular controls.

    @category{ax}
 */

class AxVoiceAdaptationHandler: public wxObject
{
    DECLARE_CLASS( AxVoiceAdaptationHandler )

public:
    AxVoiceAdaptationHandler() {}
    ~AxVoiceAdaptationHandler() {}

    /// Override to indicate if this kind of window can be handled by this object.
    virtual bool CanHandleWindow(wxWindow* win) = 0;

    /// Override to create the event handler (but not push it onto the event stack)
    virtual AxAdapter* CreateEventHandler(wxWindow* win) = 0;

    /// Override to adapt e.g. a composite control. Returns an array of the identifiers
    /// of the adapted windows.
    virtual bool Adapt(wxWindow* window, AxSpeech* speech, wxShortcutManager* shortcuts, wxArrayInt& ids);

    /// Override to unadapt e.g. a composite control. Removes the id from ids once unadapted.
    virtual bool Unadapt(wxWindow* window, wxArrayInt& ids);

    /// Override to prevent recursive adaptation, e.g. into a composite.
    virtual bool CanRecursivelyAdapt(wxWindow* WXUNUSED(window)) const { return true; }
};

WX_DEFINE_ARRAY_PTR(AxVoiceAdaptationHandler*, AxVoiceAdaptationHandlerArray);

/**
    @class AxSelfVoicing

    A class that can voice-enable an existing window containing controls,
    by inserting event handlers.
    
    You can add an object of this class to your own dialogs and panels,
    and call Adapt after control creation. This will cause adapters to be inserted into
    the control event chains, intercepting events and talking as appropriate.

    @category{ax}
 */

class AxSelfVoicing: public wxEvtHandler
{
    DECLARE_DYNAMIC_CLASS( AxSelfVoicing )
    DECLARE_EVENT_TABLE()

public:
// Constructors

    AxSelfVoicing(wxWindow* window = NULL, AxSpeech* speech = NULL, wxShortcutManager* shortcutManager = NULL);

    ~AxSelfVoicing();

// Operations

    void Init();

    /// Adapt the controls in this window. If window is passed,
    /// it becomes the window associated with the adpater.
    /// Flags can be AxAdaptDefault, or AxAdaptSetAccelerators, which
    /// causes wxWindow::SetAcceleratorTable to be called using the
    /// current shortcut manager. However, use AxAdaptSetAccelerators with caution
    /// since it can conflict with shortcut processing by adapters and
    /// cause shortcuts to stop working.

    virtual bool Adapt(wxWindow* window = NULL, int flags = AxAdaptDefault);

    /// Restore the controls in this window.
    virtual bool UndoAdaptation();

    /// Adapt a single window, optionally recursing.
    virtual bool AdaptWindow(wxWindow* window, bool recurse = true);

    /// Unadapt a single window, optionally recursing.
    virtual bool UnadaptWindow(wxWindow* window, bool recurse = true);

    void SetSpeech(AxSpeech* speech) { m_speech = speech; }
    AxSpeech* GetSpeech() const { return m_speech; }

    void SetWindow(wxWindow* win) { m_window = win; }
    wxWindow* GetWindow() const { return m_window; }

    void SetShortcutManager(wxShortcutManager* shortcutManager) { m_shortcutManager = shortcutManager; }
    wxShortcutManager* GetShortcutManager() const { return m_shortcutManager; }

    // returns true if we have adapted the window.
    bool IsAdapted() const { return m_adapted; }

    /// Returns the array of handlers that can create event handlers for specific windows.
    static AxVoiceAdaptationHandlerArray& GetAdaptationHandlers() { return m_voiceAdaptationHandlers; }

    /// Adds an adaptation handler.
    static void AddAdaptationHandler(AxVoiceAdaptationHandler* handler) { m_voiceAdaptationHandlers.Add(handler); }

    /// Inserts an adaptation handler at the front of the list.
    static void InsertAdaptationHandler(AxVoiceAdaptationHandler* handler) { m_voiceAdaptationHandlers.Insert(handler, 0); }

    /// Adds standard handlers
    static void AddStandardAdaptationHandlers();

    /// Clears the handlers
    static void ClearAdaptationHandlers();

    /// Finds a handler for the given window
    static AxVoiceAdaptationHandler* FindAdaptationHandler(wxWindow* window);

    /// Finds a voice event handler in the given window's event stack
    static AxAdapter* FindAdapter(wxWindow* window);

    /// Adds an adapter to a window
    static bool AddAdapter(wxWindow* window, AxAdapter* adapter);

    /// Removes and deletes an adapter
    static bool RemoveAdapter(wxWindow* window, AxAdapter* adapter = NULL);

protected:
    
    // Handlers that can create event handlers for specific windows.
    static AxVoiceAdaptationHandlerArray    m_voiceAdaptationHandlers;

    wxWindow*                               m_window;
    AxSpeech*                               m_speech;
    wxShortcutManager*                      m_shortcutManager;

    // true if we have adapted the window.
    bool                                    m_adapted;

    // Identifiers of the controls we've adapted, so we don't
    // unadapt ones that have been adapted using a different method
    // (for example an AxTextCtrl)
    wxArrayInt                              m_adaptedCtrls;
};

#endif
    // _AX_UI_ADAPTATION_H_
